package com.bysreg;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.LinkedList;
import java.util.Stack;
import java.io.IOException;

public class Crawler {
    //kata kunci pencarian
    final private char[][] query;
    
    //lokasi pencarian dimulai
    final private String searchpath;

    //algoritma yang dipakai untuk pencocokan string
    final private int matchalgo;
    public static int KMP = 0;
    public static int BM=1;

    //algoritma yang dipakai untuk pencarian 
    final private int searchalgo;
    public static int BFS = 0;
    public static int DFS = 1;

    //parameter tambahan untuk pencocokan string
    final private int matchparam;

    //maksimum panjang karakter preview suatu hasil
    private static final int MAX_PREVIEW_CHAR = 154;
    //berapa banyak maksimum kata yang ditampilkan jika ketemu kata kunci
    private static final int MAX_A_MATCH_PREVIEW_CHAR = 90;

    //penampung hasil pencarian
    final LinkedList<Result> res;

    public Crawler(String query,String searchpath,int matchalgo, int searchalgo, int matchparam) {
        String[] squery = query.split(" ");
        this.query = new char[squery.length][];
        for(int i=0;i<squery.length;i++) {
            this.query[i] = squery[i].toCharArray();
        }
        this.searchpath = searchpath;
        this.matchalgo = matchalgo;
        this.searchalgo = searchalgo;
        this.matchparam = matchparam;
        res = new LinkedList<Result>();
    }

    public Result[] crawl() {
        if(searchalgo==BFS) {
            bfscrawl();
        }else if(searchalgo==DFS) {
            dfscrawl();
        }
        return res.toArray(new Result[0]);
    }

    private void bfscrawl() {
        LinkedList<File> sfile = new LinkedList<File>();
        sfile.addLast(new File(searchpath));
        File node;
        while(!sfile.isEmpty()) {
            node = sfile.removeFirst();
            if(node.isDirectory()) {
                for(File f : node.listFiles()) {
                    sfile.addLast(f);
                }
            }else{
                match(node,res);
            }
        }
    }

    private void dfscrawl() {
        Stack<File> sfile = new Stack<File>();
        sfile.push(new File(searchpath));
        File node;
        while(!sfile.isEmpty()) {
            node = sfile.pop();
            if(node.isDirectory()) {
                for(File f : node.listFiles()) {
                    sfile.push(f);
                }
            }else{
                match(node,res);
            }
        }
    }

    private void match(File file, LinkedList<Result> res) {
        int i=0;//indeks karakter string yang sedang dibaca        
        int k=0;//indeks query mana yang sedang dibandingkan
        int m=0;//indeks karakter query yang dibandingkan        
        Result ares;
        StringBuilder preview = new StringBuilder(MAX_PREVIEW_CHAR);
        LinkedList<Integer> miprev = new LinkedList<Integer>();//array yang tiap elemennya berisi index match di string preview
        LinkedList<Integer> mcprev = new LinkedList<Integer>();//array yang tiap elemennya berisi char count match(bersesuaian dengan miprev)
        try{                        
            char[] s = readFileAsString(file).replaceAll("\n", " ").toCharArray();
            while(i<s.length && preview.length()<MAX_PREVIEW_CHAR) {
                if(s[i]==query[k][0]) {
                    m=1;                    
                    while(i+m<s.length) {
                        if(s[i+m]!=query[k][m]) {                            
                            break;
                        }
                        if(m==query[k].length-1 && s[i+m]==query[k][m]) {
                            //System.out.println("ketemu di " + file.getName() + " indeks : " + i);
                            addPreview(preview,s,i,query[k].length, miprev, mcprev);
                            break;
                        }
                        m++;
                    }                    
                }
                i++;
            }            
        }catch(IOException e) {            
        }
        if(preview.length()>0) {
            ares = new Result(file.getName(),file.getName(),file.getPath(),preview.toString(),miprev.toArray(new Integer[0]),mcprev.toArray(new Integer[0]));
            res.add(ares);            
        }
    }

    private static void addPreview(StringBuilder preview, char[] s, int idx, int count, LinkedList<Integer> miprev, LinkedList<Integer> mcprev) {
        //jika kata kunci ditemukan di kalimat awal file
        int i;               
        if(idx==0) {//berada di ujung awal teks
            i=idx;
            miprev.add(new Integer(preview.length()));
            mcprev.add(new Integer(count));
            while(i<idx+MAX_A_MATCH_PREVIEW_CHAR && i<s.length  && preview.length()+3<MAX_PREVIEW_CHAR) {
                preview.append(s[i]);                
                i++;
            }
            preview.append("...");
        }else if(idx+count==s.length) { //berada di ujung akhir teks            
            int temp = preview.length();
            i=idx+count-1;
            while(i>=idx-(MAX_A_MATCH_PREVIEW_CHAR-count) && i>=0  && preview.length()<MAX_PREVIEW_CHAR) {                
                preview.insert(temp, s[i]);
                i--;
            }
            miprev.add(new Integer(preview.length()-count));
            mcprev.add(new Integer(count));
        }else{//berada di tengah teks
            //ambil dari kiri dulu abis itu ke kanan
            i=idx+count-(MAX_A_MATCH_PREVIEW_CHAR/2);
            mcprev.add(new Integer(count));
            if((MAX_A_MATCH_PREVIEW_CHAR/2)>(MAX_PREVIEW_CHAR-preview.length())) {//kondisi dimana jumlah karakter tersisa untuk preview lebih kecil dari yang diinginkan
                i = idx+count-(MAX_PREVIEW_CHAR-preview.length());
                miprev.add(new Integer(MAX_PREVIEW_CHAR-count));
            }else{
                miprev.add(new Integer(preview.length()+(MAX_A_MATCH_PREVIEW_CHAR/2)-count));                
            }
            while(i<idx+(MAX_A_MATCH_PREVIEW_CHAR/2) && i<s.length && preview.length()+3<MAX_PREVIEW_CHAR) {
                preview.append(s[i]);
                i++;
            }
            preview.append("...");
        }        
    }

    private static String readFileAsString(File file) throws java.io.IOException{
        byte[] buffer = new byte[(int) file.length()];
        BufferedInputStream f = null;
        try {
            f = new BufferedInputStream(new FileInputStream(file));
            f.read(buffer);
        } finally {
            if (f != null) try { f.close(); } catch (IOException ignored) { }
        }
        return new String(buffer);
    }
    
}

//awfdawf